﻿namespace LightCAD.Core.Elements.Basic
{
    public class LcTableCell : LcElement
    {
        // 文字
        public string Text;
        // 开始坐标
        public Vector2 FirstPoint;
        // 结束坐标
        public Vector2 EndPoint;
        // 列宽
        public double ColumnWidth;
        // 行高
        public double RowHeight;
        // 行数
        public int RowIndex;
        // 列数
        public int ColIndex;
        // 跨列
        public int ColRange;
        // 跨行
        public int RowRange;

        public Box2 Box2D;

        public LcTableCell()
        {
            this.Type = BuiltinElementType.TableCell;
        }

        public override LcElement Clone()
        {
            var clone = new LcTableCell();
            clone.Copy(this);
            return clone;
        }

        public void Init(Vector2 tableFirstPoint, double rowHeight, double columnWidth, int rowIndex, int colIndex, int colRange = 1, int rowRange = 1)
        {
            this.RowHeight = rowHeight * rowRange;
            this.ColumnWidth = columnWidth * colRange;
            this.RowIndex = rowIndex;
            this.ColIndex = colIndex;
            this.ColRange = colRange;
            this.RowRange = rowRange;

            this.FirstPoint = new Vector2(tableFirstPoint.X + ColIndex * this.ColumnWidth, tableFirstPoint.Y - RowIndex * this.RowHeight);
            this.EndPoint = new Vector2(this.FirstPoint.X + this.ColumnWidth, this.FirstPoint.Y - this.RowHeight);

            this.Box2D = new Box2().SetFromPoints(this.FirstPoint, this.EndPoint);
        }

        public void Set(Vector2 start = null, Vector2 end = null, bool fireChangedEvent = true)
        {
            if (!fireChangedEvent)
            {
                if (start != null) this.FirstPoint = start;
                if (end != null) this.EndPoint = end;
            }
            else
            {
                bool chg_start = (start != null && start != this.FirstPoint);
                if (chg_start)
                {
                    OnPropertyChangedBefore(nameof(start), this.FirstPoint, start);
                    var oldValue = this.FirstPoint;
                    this.FirstPoint = start;
                    OnPropertyChangedAfter(nameof(start), oldValue, this.FirstPoint);
                }

                bool chg_end = (end != null && end != this.EndPoint);
                if (chg_end)
                {
                    OnPropertyChangedBefore(nameof(end), this.EndPoint, end);
                    var oldValue = this.EndPoint;
                    this.EndPoint = end;
                    OnPropertyChangedAfter(nameof(end), oldValue, this.EndPoint);
                }
                this.Box2D = new Box2().SetFromPoints(this.FirstPoint, this.EndPoint);
            }
        }

        public void Set(Vector2 tableStart = null, double? columnWidth = null, double? rowHeight = null, 
            int? colRange = null, int? rowRange = null, string text = null, bool fireChangedEvent = true)
        {
            Vector2 start = null;
            Vector2 end = null;

            if (tableStart != null)
            {
                var tmpRowRang = rowRange ?? this.RowRange;
                var tmpColRang = colRange ?? this.ColRange;
                var tmpWidth = columnWidth ?? this.ColumnWidth;
                var tmpHeight = rowHeight ?? this.RowHeight;
                tmpWidth *= tmpColRang;
                tmpHeight *= tmpRowRang;

                start = new Vector2(tableStart.X + this.ColIndex * tmpWidth, tableStart.Y - this.RowIndex * tmpHeight);
                end = new Vector2(this.FirstPoint.X + tmpWidth, this.FirstPoint.Y - tmpHeight);
            }

            if (!fireChangedEvent)
            {
                if (start != null) this.FirstPoint = start;
                if (end != null) this.EndPoint = end;
            }
            else
            {
                bool chg_start = (start != null && start != this.FirstPoint);
                if (chg_start)
                {
                    OnPropertyChangedBefore(nameof(start), this.FirstPoint, start);
                    var oldValue = this.FirstPoint;
                    this.FirstPoint = start;
                    OnPropertyChangedAfter(nameof(start), oldValue, this.FirstPoint);
                }

                bool chg_end = (end != null && end != this.EndPoint);
                if (chg_end)
                {
                    OnPropertyChangedBefore(nameof(end), this.EndPoint, end);
                    var oldValue = this.EndPoint;
                    this.EndPoint = end;
                    OnPropertyChangedAfter(nameof(end), oldValue, this.EndPoint);
                }
                this.Box2D = new Box2().SetFromPoints(this.FirstPoint, this.EndPoint);

                var chg = (colRange != null && colRange != this.ColRange);
                if (chg)
                {
                    OnPropertyChangedBefore(nameof(colRange), this.ColRange, colRange);
                    var oldValue = this.ColRange;
                    this.ColRange = colRange.Value;
                    OnPropertyChangedAfter(nameof(colRange), oldValue, this.ColRange);
                }

                chg = (rowRange != null && rowRange != this.RowRange);
                if (chg)
                {
                    OnPropertyChangedBefore(nameof(rowRange), this.RowRange, rowRange);
                    var oldValue = this.RowRange;
                    this.RowRange = rowRange.Value;
                    OnPropertyChangedAfter(nameof(rowRange), oldValue, this.RowRange);
                }

                chg = (columnWidth != null && columnWidth != this.ColumnWidth);
                if (chg)
                {
                    OnPropertyChangedBefore(nameof(columnWidth), this.ColumnWidth, columnWidth);
                    var oldValue = this.ColumnWidth;
                    this.ColumnWidth = columnWidth.Value * this.ColRange;
                    OnPropertyChangedAfter(nameof(columnWidth), oldValue, this.ColumnWidth);
                }

                chg = (rowHeight != null && rowHeight != this.RowHeight);
                if (chg)
                {
                    OnPropertyChangedBefore(nameof(rowHeight), this.RowHeight, rowHeight);
                    var oldValue = this.RowHeight;
                    this.RowHeight = rowHeight.Value * this.RowRange;
                    OnPropertyChangedAfter(nameof(rowHeight), oldValue, this.RowHeight);
                }

                chg = (text != null && text != this.Text);
                if (chg)
                {
                    OnPropertyChangedBefore(nameof(text), this.Text, text);
                    var oldValue = this.Text;
                    this.Text = text;
                    OnPropertyChangedAfter(nameof(text), oldValue, this.Text);
                }
            }
        }

        public override void Translate(double dx, double dy)
        {
            var ts = new Vector2(this.FirstPoint.X + dx, this.FirstPoint.Y + dy);
            var te = new Vector2(this.EndPoint.X + dx, this.EndPoint.Y + dy);
            Set(ts, te);
            ResetBoundingBox();
        }

        public override Box2 GetBoundingBox()
        {
            return new Box2().SetFromPoints(this.FirstPoint, this.EndPoint);
        }
        public override Box2 GetBoundingBox(Matrix3 matrix)
        {
            return new Box2().SetFromPoints(matrix.MultiplyPoint(this.FirstPoint), matrix.MultiplyPoint(this.EndPoint));
        }

        public override bool IntersectWithBox(Polygon2d testPoly, List<RefChildElement> intersectChildren = null)
        {
            var thisBox = this.BoundingBox;
            if (!thisBox.IntersectsBox(testPoly.BoundingBox))
            {
                //如果元素盒子，与多边形盒子不相交，那就可能不相交
                return false;
            }
            return GeoUtils.IsPolygonIntersectLine(testPoly.Points, this.FirstPoint, this.EndPoint)
                || GeoUtils.IsPolygonContainsLine(testPoly.Points, this.FirstPoint, this.EndPoint);
        }

        public override bool IncludedByBox(Polygon2d testPoly, List<RefChildElement> includedChildren = null)
        {
            var thisBox = this.BoundingBox;
            if (!thisBox.IntersectsBox(testPoly.BoundingBox))
            {
                return false;
            }
            return GeoUtils.IsPolygonContainsLine(testPoly.Points, this.FirstPoint, this.EndPoint);
        }
    }
}
